package domain;

import domain.player.Disc;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * A class implementing the interface GameRules for 4-in-a-row game.
 */
public class Rules4InARow implements GameRules {
	/** The number of discs needed in a row to win. */
	public static final int NEEDED_DISCS = 4;

	private GameGrid grid;

	/** Constructor. */
	public Rules4InARow(GameGrid gameGrid) {
		this.grid = gameGrid;
		//grid = gameGrid.getGameGrid();
	}

	/** Returns the winner */
	@Override
	public Disc getWinner() {
		for (int i = GameGrid.GRID_ROWS - 1; i >= 0; i--) {
			for (int j = GameGrid.GRID_COLUMNS - 1; j >= 0; j--) {
				if ((grid.getDisc(i, j) != Disc.NONE) && hasWon(i, j)) {
					return grid.getDisc(i, j);
				}
			}
		}
		return Disc.NONE;
	}

	/** Returns an Interator containing all the points included in the winnin row/-s */
	@Override
	public Iterator getWinnerPoints() {
		List<Point> winnerRows = new ArrayList<Point>();

		for (int i = GameGrid.GRID_ROWS - 1; i >= 0; i--) {
			for (int j = GameGrid.GRID_COLUMNS - 1; j >= 0; j--) {
				if ((grid.getDisc(i, j) != Disc.NONE) && hasWon(i, j)) {
					winnerRows.add(new Point(i, j));
				}
			}
		}
		return winnerRows.iterator();
	}

	@Override
	public boolean hasWon() {
		for (int i = GameGrid.GRID_ROWS - 1; i >= 0; i--) {
			for (int j = GameGrid.GRID_COLUMNS - 1; j >= 0; j--) {
				if ((grid.getDisc(i, j) != Disc.NONE) && hasWon(i, j)) {
					return true;
				}
			}
		}
		return false;
	}

	@Override
	public boolean hasWon(int x, int y) {
		return (checkRow(x, y)
				|| checkColumn(x, y)
				|| checkDiagonalLeft(x, y)
				|| checkDiagonalRight(x, y));
	}

	/**
	 * Returns true if the disc at (row, column) is part of a horizontal row
	 * with NEEDED_DISCS of the same color/disc.
	 *
	 * @author Caroline
	 */
	private boolean checkRow(int row, int column) {
		int count = 1, col;
		Disc color = grid.getDisc(row, column);

		col = column - 1;
		while (col >= 0 && grid.getDisc(row, col) == color) {
			count++;
			col--;
		}

		col = column + 1;
		while (col < GameGrid.GRID_COLUMNS && grid.getDisc(row, col) == color) {
			count++;
			col++;
		}

		return (count >= NEEDED_DISCS);
	}

	/**
	 * Returns true if the disc at (row, column) is part of a diagonal row,
	 * starting from the top left going down, with NEEDED_DISCS of the same
	 * color/disc.
	 *
	 * @author Caroline
	 */
	private boolean checkDiagonalLeft(int row, int column) {
		int count = 1, currentRow, currentCol;
		Disc color = grid.getDisc(row, column);

		currentRow = row - 1;
		currentCol = column - 1;
		while (currentRow >= 0 && currentCol >= 0 && grid.getDisc(currentRow, currentCol) == color) {
			count++;
			currentRow--;
			currentCol--;
		}

		currentRow = row + 1;
		currentCol = column + 1;
		while (currentRow < GameGrid.GRID_ROWS && currentCol < GameGrid.GRID_COLUMNS && grid.getDisc(currentRow, currentCol) == color) {
			count++;
			currentRow++;
			currentCol++;
		}

		return (count >= NEEDED_DISCS);
	}

	/**
	 * Returns true if the disc at (row, column) is part of a diagonal row,
	 * starting from the top right going down, with NEEDED_DISCS of the same
	 * color/disc.
	 *
	 * @author Caroline
	 */
	private boolean checkDiagonalRight(int row, int column) {
		int count = 1, currentRow, currentCol;
		Disc color = grid.getDisc(row, column);

		currentRow = row - 1;
		currentCol = column + 1;
		while ((currentRow >= 0 && currentCol < GameGrid.GRID_COLUMNS) && grid.getDisc(currentRow, currentCol) == color) {
			count++;
			currentRow--;
			currentCol++;
		}

		currentRow = row + 1;
		currentCol = column - 1;
		while ((currentRow < GameGrid.GRID_ROWS && currentCol >= 0) && grid.getDisc(currentRow, currentCol) == color) {
			count++;
			currentRow++;
			currentCol--;
		}

		return (count >= NEEDED_DISCS);
	}

	/**
	 * Returns true if the disc at (row, column) is part of a vertical row
	 * with NEEDED_DISCS of the same color/disc.
	 *
	 * @author Caroline
	 */
	private boolean checkColumn(int row, int column) {
		int count = 1, currentRow;
		Disc color = grid.getDisc(row, column);

		currentRow = row - 1;
		while (currentRow >= 0 && grid.getDisc(currentRow, column) == color) {
			count++;
			currentRow--;
		}

		currentRow = row + 1;
		while (currentRow < GameGrid.GRID_ROWS && grid.getDisc(currentRow, column) == color) {
			count++;
			currentRow++;
		}

		return (count >= NEEDED_DISCS);
	}

	@Override
	public boolean isValidMove(int column) {
		return !grid.isFull(column);
	}

	@Override
	public boolean isTie() {
		for (int i = 0; i < GameGrid.GRID_COLUMNS; i++) {
			if (!grid.isFull(i)) {
				return false;
			}
		}
		return true;
	}
}
